package com.gitee.feizns.dynamic.reflect;

import com.gitee.feizns.dynamic.Ex;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;

/**
 * 方法反射工具类
 * <p>
 *     在一个类上查找方法时，如果使用的是使用{@link Class#getMethod(String, Class[])}
 *     仅能查找public方法（包括继承来的），而使用{@link Class#getDeclaredMethod(String, Class[])}
 *     则仅能查找到该类中显式声明的方法（包括私有方法，但无法获取到从父类和接口中继承来的任何方法）
 * </p>
 * @author feizns
 * @since 2019/5/15
 */
public abstract class Methods {

    /**
     * 在整个继承链中筛选匹配规则的方法
     * @param type 需要查找方法的类型
     * @param filter 过滤规则
     * @return 返回整个继承链中匹配filter规则的方法
     */
    public static List<Method> of(Class<?> type, Predicate<Method> filter) {
        List<Method> result = new ArrayList<>();
        while ( type != null ) {
            Arrays.stream(type.getDeclaredMethods())
                    .filter(filter)
                    .forEach(result::add);
            type = type.getSuperclass();
        }
        return result;
    }

    /**
     * <b>在一个类的继承结构中查找指定名称的方法（包括私有方法）</b>
     * @see Class#getDeclaredMethod(String, Class[])
     * @param type 需要查找方法的类型
     * @param name 方法名称
     * @param parameterTypes 方法参数类型
     * @return {@link Method}
     */
    public static Method of(Class<?> type, String name, Class<?>... parameterTypes) {
        NoSuchMethodException e = null;
        while ( type != null ) {
            try {
                return Reflects.accessible(type.getDeclaredMethod(name, parameterTypes));
            } catch (NoSuchMethodException ex) {
                type = type.getSuperclass();
                e = ex;
            }
        }
        throw new RuntimeException(e);
    }

    /**
     * <b>获取公共方法</b>
     * @param type {@link Class}
     * @param name 方法名称
     * @param parameterTypes 方法参数类型列表
     * @return 返回方法元信息
     */
    public static Method find(Class<?> type, String name, Class<?>... parameterTypes) {
        return Ex.tryCatch(() -> Reflects.accessible(type.getMethod(name, parameterTypes)))
                .ignoreException(NoSuchMethodException.class)
                .orElse(null);
    }

    /**
     * <b>在类中查找所有的函数</b>
     * @param type 查找的类型
     * @param filter 过滤有用的方法
     * @return 返回过滤后的方法
     */
    public static Stream<Method> find(Class<?> type, Predicate<Method> filter) {
        return Arrays.stream(type.getMethods()).filter(filter);
    }

    /**
     * <b>调用静态方法</b>
     * @param method 方法
     * @param params 方法参数
     * @return 方法返回值
     */
    public static Object invokeStatic(Method method, Object... params) {
        return Ex.tryCatch(() -> Reflects.accessible(method).invoke(null, params)).orElse(null);
    }

    /**
     * <b>调用方法</b>
     * @param obj 调用方法的实例对象
     * @param methodName 方法名称
     * @param params 方法参数
     * @return 方法返回值
     */
    public static Object invoke(Object obj, String methodName, Object... params) {
        return Ex.tryCatch(() -> {
            Method method = obj.getClass().getMethod(methodName, Classes.of(params).toArray(new Class[]{}));
            return Reflects.accessible(method).invoke(obj, params);
        }).orElse(null);
    }

    /**
     * <b>调用方法</b>
     * @param obj 调用方法的实例对象
     * @param method 方法
     * @param params 方法参数
     * @return 返回方法的返回值
     */
    public static Object invoke(Object obj, Method method, Object... params) {
        return Ex.tryCatch(() -> Reflects.accessible(method).invoke(obj, params)).orElse(null);
    }

    /**
     * <b>获取方法返回值类型上的泛型</b>
     * <p>
     *     例：如有方法的返回值类型为 List<String> -> 则返回 [String.class]<br/>
     *     例：如有方法的返回值类型为 List{@literal <String>} -> 则返回 [String.class]
     * </p>
     * @param method {@link Method}
     * @return 返回方法返回值类型中的泛型类型列表
     */
    public static Class<?>[] ofReturnTypeGenericTypes(Method method) {
        return Classes.ofGenericTypes(method.getGenericReturnType());
    }

}
